/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.fresh.bus.canoe.message.support;

import com.fresh.bus.canoe.exception.ServerException;
import com.fresh.bus.canoe.message.MessageCodec;
import com.fresh.bus.canoe.message.Response;
import com.fresh.bus.canoe.session.Role;
import com.google.common.collect.MapMaker;
import java.util.Collection;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author panzd
 */
public abstract class AbstractMessageCodec implements MessageCodec {
    private static final Logger log = LoggerFactory.getLogger(AbstractMessageCodec.class);
    private ConcurrentMap<Response, byte[]> responseEncodeCache;

    public AbstractMessageCodec() {
        responseEncodeCache = new MapMaker().weakKeys().expiration(1, TimeUnit.SECONDS).makeMap();
    }

    @Override
    public byte[] encodeResponse(Response response) throws ServerException {
        if (response == null) {
            return null;
        }

        long startTime = System.currentTimeMillis();

        byte[] responseBytes;

        Collection<Role> recievers = response.getRecievers();
        if (recievers == null || recievers.size() <= 1) {
            responseBytes = doEncodeResponse(response);
        } else {
            responseBytes = getCachedBytes(response);
        }

        log.debug("decode " + response + " cost " + (System.currentTimeMillis() - startTime) + "ms");

        return responseBytes;
    }

    abstract protected byte[] doEncodeResponse(Response response) throws ServerException;

    private byte[] getCachedBytes(Response response) {
        byte[] bytes = responseEncodeCache.get(response);

        if (bytes == null) {
            synchronized (response) {
                bytes = responseEncodeCache.get(response);

                if (bytes == null) {
                    bytes = doEncodeResponse(response);
                    responseEncodeCache.put(response, bytes);
                }
            }
        }

        return bytes;
    }
}
